#include <sys/epoll.h>
#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <errno.h>
#include "php.h"
#include "nginx.h"
#include "server.h"

extern int silent_running;


typedef struct _nginx_event {
	int epfd;
	int max_event;
	struct epoll_event *events;
} nginx_event_t;

typedef struct _nginx_event_node {
	int fd;
	int type;
} nginx_event_node_t;

int slt_nginx_reactor_add(reactor_t *nginx_reactor, int fd, int type);
void slt_nginx_reactor_del(reactor_t *nginx_reactor, int fd);
void slt_nginx_reactor_wait(reactor_t *nginx_reactor);
void slt_nginx_reactor_set(reactor_t *nginx_reactor, int type, handler_func_t func);
void slt_nginx_reactor_create(reactor_t *nginx_reactor, int max_event);

void slt_nginx_reactor_del(reactor_t *nginx_reactor, int fd) {
    nginx_event_t *object = nginx_reactor->object;
    struct epoll_event event_node;
    int ret;

    event_node.data.fd = fd;
    event_node.events = EPOLLIN|EPOLLET;

    epoll_ctl(object->epfd, EPOLL_CTL_DEL, fd, &event_node);
}

static void slt_nginx_accept(reactor_t *nginx_reactor, event_emit_node_t *emit_node) {
	slt_server_t *serv = nginx_reactor->ptr;

	serv->factory.process(&(serv->factory), emit_node);
}

void *slt_nginx_reactor_start(void *reactor) {
	reactor_t *nginx_reactor = reactor;
	slt_server_t *serv = nginx_reactor->ptr;
	int max_conn = serv->max_conn;

	slt_nginx_reactor_create(nginx_reactor, (max_conn/serv->poll_thread_num) + 1);

	nginx_reactor->set(nginx_reactor, SLT_NGINX_EVENT_ACCEPT, slt_nginx_accept);
	nginx_reactor->wait(nginx_reactor);
	nginx_reactor->free(nginx_reactor);

	pthread_exit((void *)NULL);
}

inline void slt_nginx_reactor_free(reactor_t *nginx_reactor) {
	nginx_event_t *object;

	object = nginx_reactor->object;
	close(object->epfd);
	efree(object->events);
	efree(object);
}

void slt_nginx_reactor_create(reactor_t *nginx_reactor, int max_event) {
	nginx_event_t *object = emalloc(sizeof(nginx_event_t));

	object->max_event = 0;
	object->epfd = epoll_create(512);
	object->events = ecalloc(max_event, sizeof(struct epoll_event));
	
	bzero(nginx_reactor->handlers, sizeof(nginx_reactor->handlers));
	nginx_reactor->object = object;
	nginx_reactor->add = slt_nginx_reactor_add;
	nginx_reactor->wait = slt_nginx_reactor_wait;
	nginx_reactor->set = slt_nginx_reactor_set;
	nginx_reactor->free = slt_nginx_reactor_free;
	nginx_reactor->del = slt_nginx_reactor_del;
}

inline void slt_nginx_reactor_set(reactor_t *nginx_reactor, int type, handler_func_t func) {
	nginx_reactor->handlers[type] = func;
}

void slt_nginx_reactor_wait(reactor_t *nginx_reactor) {
	nginx_event_t *object = nginx_reactor->object;
	nginx_event_node_t event_node;
	event_emit_node_t emit_node;
	int ret;
	int i;

	while (silent_running) {
		ret = epoll_wait(object->epfd, object->events, object->max_event + 1, 3000);
		if (ret == -1) {
			if (errno == EINTR) {
				continue ;
			}
			break ;
		} else if (ret == 0) {
			continue ;
		}
		
		for (i = 0; i < ret; i++) {
			if (object->events[i].events && EPOLLIN) {
				memcpy(&event_node, &(object->events[i].data.u64), sizeof(uint64_t));
				emit_node.fd = event_node.fd;
				emit_node.type = event_node.type;
				emit_node.from_id = nginx_reactor->id;
				nginx_reactor->handlers[event_node.type](nginx_reactor, &emit_node);
			}
		}
	}
}

int slt_nginx_reactor_add(reactor_t *nginx_reactor, int fd, int type) {
	nginx_event_node_t event_data;
	nginx_event_t *object = nginx_reactor->object;
	struct epoll_event event_node;
	int ret;

	event_data.fd = fd;
	event_data.type = type;
	event_node.events = EPOLLIN|EPOLLET;
	memcpy(&(event_node.data.u64), &event_data, sizeof(nginx_event_node_t));
	
	ret = epoll_ctl(object->epfd, EPOLL_CTL_ADD, fd, &event_node);
	if (ret == -1) {
		return -1;
	}
	object->max_event++;
	return 0;
}
